﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Graphics.PackedVector;

using WindowsGameInput;



namespace WindowsGame
{
	public interface IGameLogicService
	{
		//ICamera Camera { get; }
		ILights Lights { get; }
		IHeadUpDisplayLogic HeadUpDisplayLogic { get; }
		Matrix ProjectionMat { get; }
		//IObjectLogic ObjectLogic { get; }
		GameState.State GameState_{ get; }
		GameState.Terrain Terrain_ { get; }
	}



	class GameLogic : Microsoft.Xna.Framework.DrawableGameComponent, IGameLogicService
	{
		//Camera camera = new Camera();
		HeadUpDisplayLogic headUpDisplayLogic = new HeadUpDisplayLogic();
		LightsHandler lightsHandler = new LightsHandler();

		Matrix projectionMat = Matrix.Identity;

		IInputEventService inputEventService;
		IGameResourcesService gameResourcesService;
		IGameOptionsService gameOptionsService;

		//ObjectLogic objectLogic;

		//public ICamera Camera { get { return camera; } }
		public ILights Lights { get { return lightsHandler; } }
		public IHeadUpDisplayLogic HeadUpDisplayLogic { get { return headUpDisplayLogic; } }
		public Matrix ProjectionMat { get { return projectionMat; } }
		//public IObjectLogic ObjectLogic { get { return objectLogic; } }
		public GameState.State GameState_ { get { return GameState.currentState; } }
		public GameState.Terrain Terrain_ { get { return GameState.terrain; } }

		//GameState.State gameState;



		public GameLogic(Game game)
			: base(game)
		{
		}



		protected override void Dispose(bool disposing)
		{
			Game.Services.RemoveService(typeof(IGameLogicService));

			base.Dispose(disposing);
		}



		//protected void OnEnterMenu()
		//{
		//    foreach(GameLogic logic in Game.Components.OfType<GameLogic>())
		//    {
		//        logic.Visible = false;
		//        logic.Enabled = false;
		//    }
		//    foreach(Rendering rendering in Game.Components.OfType<Rendering>())
		//    {
		//        rendering.Visible = false;
		//        rendering.Enabled = false;
		//    }
		//    //foreach(MenuHandler menuH in Game.Components.OfType<MenuHandler>())
		//    //{
		//    //    menuH.Visible = true;
		//    //    menuH.Enabled = true;
		//    //}
		//}


		public override void Initialize()
		{
			projectionMat = Matrix.CreatePerspectiveFieldOfView(MathHelper.Pi / 10.0f, 4.0f / 3.0f, 10.0f, 350.0f);

			Game.Services.AddService(typeof(IGameLogicService), this);
			gameResourcesService = Game.Services.GetService(typeof(IGameResourcesService)) as IGameResourcesService;
			inputEventService = Game.Services.GetService(typeof(IInputEventService)) as IInputEventService;
			gameOptionsService = Game.Services.GetService(typeof(IGameOptionsService)) as IGameOptionsService;

			//objectLogic = new ObjectLogic();

			inputEventService.ToggleHUD += () => headUpDisplayLogic.ToggleDisplay();
			//inputEventService.Quit += () => Game.Exit();
			//inputEventService.Quit += OnEnterMenu;

			base.Initialize();
		}



		protected override void LoadContent()
		{
			Debug.Assert(gameResourcesService != null);

			int atlasTileDim = gameResourcesService.Terrain.atlasTileDim;

			Single[,] terrainData = new Single[(atlasTileDim-1) * (int)gameResourcesService.Terrain.atlasNumActiveTile +1,
				atlasTileDim];

			HalfSingle[] terrainPatchData = new HalfSingle[atlasTileDim * atlasTileDim];

			int terrainRow = 0;
			for(int i = (int)gameResourcesService.Terrain.atlasNumActiveTile - 1; i >= 0; i--)
			{
				int atlasRow = (int)Math.Floor((double)i / gameResourcesService.Terrain.atlasNumTilePerCols);
				int atlasCol = i - atlasRow * (int)gameResourcesService.Terrain.atlasNumTilePerCols;

				int paddedTileDim = atlasTileDim + gameResourcesService.Terrain.atlasPadding;
				int startRow = atlasRow * paddedTileDim;
				int startCol = atlasCol * paddedTileDim;

				Rectangle patchRect = new Rectangle(startCol, startRow, atlasTileDim, atlasTileDim);
				gameResourcesService.Terrain.heightMapAtlas.GetData<HalfSingle>(0, patchRect, terrainPatchData,
					0, terrainPatchData.Count());

				int patchIndex = i == 0 ? 0 : atlasTileDim;	// Mantains last patch first row only
				int patchRow = i == 0 ? 0 : 1;

				for(; patchRow < atlasTileDim; patchRow++, terrainRow++)
					for(int patchCol = 0; patchCol < atlasTileDim; patchCol++, patchIndex++)
						terrainData[terrainRow, patchCol] = terrainPatchData[patchIndex].ToSingle() *
							gameResourcesService.Terrain.heightScale;
			}

			BoundingSphere[] playerBS = new BoundingSphere[gameResourcesService.Object.model.Meshes.Count];
			BoundingSphere[] enemiesBS = new BoundingSphere[gameResourcesService.Enemies.model.Meshes.Count];
			BoundingSphere[] bulletBS = new BoundingSphere[gameResourcesService.Bullet.model.Meshes.Count];
			uint m = 0;
			foreach(var mesh in gameResourcesService.Object.model.Meshes)
				playerBS[m++] = mesh.BoundingSphere.Transform(Matrix.CreateScale(1.4f));
			m = 0;
			foreach(var mesh in gameResourcesService.Enemies.model.Meshes)
				enemiesBS[m++] = mesh.BoundingSphere.Transform(Matrix.CreateScale(1.4f));
			m = 0;
			foreach(var mesh in gameResourcesService.Bullet.model.Meshes)
				bulletBS[m++] = mesh.BoundingSphere.Transform(Matrix.CreateScale(2.1f));

			if(!gameOptionsService.Loaded)
				GameStateUpdate.InitializeState(playerBS, enemiesBS, bulletBS, gameOptionsService.Difficult);

			GameStateUpdate.InitializeTerrain(terrainData, gameResourcesService.Terrain.cellSpacing);
		}



		protected override void UnloadContent()
		{
			// Unload any non ContentManager content here
		}



		public override void Update(GameTime gameTime)
		{
			headUpDisplayLogic.Update(gameTime);

			float dT = (float)gameTime.ElapsedGameTime.TotalSeconds;

			if(dT > 0.0f)
			{
				GameStateUpdate.UpdateState(dT);
				GameStateUpdate.UpdateScript();

				//objectLogic.Update(gameTime);
				//lightsHandler.Update(gameTime);

				Variable.commit_variable_updates();
			}

			base.Update(gameTime);
		}
	}
}